package com.educate.common.web;

import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

public class PageImpl<T> implements Page<T>, Serializable {

    private static final long serialVersionUID = 3130414894668119013L;

    private List<T> content = new ArrayList<>();
    private long total;
    private int page;
    private int size;

    public PageImpl() {
    }

    public PageImpl(List<T> content) {
        this(content, Pageable.unpaged(), null == content ? 0L : (long) content.size());
    }

    public PageImpl(List<T> content, Pageable pageable, long total) {
        this.content.addAll(content);
        this.page = pageable.getPageNumber();
        this.size = pageable.getPageSize();

        this.total = pageable.toOptional().filter((it) -> {
            return !content.isEmpty();
        }).filter((it) -> {
            return it.getOffset() + (long) it.getPageSize() > total;
        }).map((it) -> {
            return it.getOffset() + (long) content.size();
        }).orElse(total);
    }

    public PageImpl(List<T> content, long total, int page, int size) {
        this.content = content;
        this.total = total;
        this.page = page;
        this.size = size;
    }

    @Override
    public int getTotalPages() {
        return this.getSize() == 0 ? 1 : (int) Math.ceil((double) this.total / (double) this.getSize());

    }

    @Override
    public long getTotalElements() {
        return this.total;
    }

    @Override
    public int getNumber() {
        return page;
    }

    @Override
    public int getSize() {
        return size;
    }

    @Override
    public int getNumberOfElements() {
        return content.size();
    }

    @Override
    public List<T> getContent() {
        return content;
    }

    @Override
    public boolean hasContent() {
        return !this.content.isEmpty();
    }

    @Override
    @JsonIgnore
    public Sort getSort() {
        return null;
    }

    @Override
    public boolean isFirst() {
        return !this.hasPrevious();
    }

    @Override
    public boolean isLast() {
        return !this.hasNext();
    }

    @Override
    public boolean hasNext() {
        return this.getNumber() + 1 < this.getTotalPages();
    }

    @Override
    public boolean hasPrevious() {
        return page > 0;
    }

    @Override
    @JsonIgnore
    public Pageable nextPageable() {
        return null;
    }

    @Override
    @JsonIgnore
    public Pageable previousPageable() {
        return null;
    }

    @Override
    @JsonIgnore
    public <U> Page<U> map(Function<? super T, ? extends U> converter) {
        List<U> content = this.stream().map(converter::apply).collect(Collectors.toList());
        return new PageImpl<>(content, this.total, this.page, this.size);
    }

    @Override
    @JsonIgnore
    public Iterator<T> iterator() {
        return content.iterator();
    }

    @Override
    @JsonIgnore
    public Pageable getPageable() {
        return null;
    }
}
